#include "os_mutex.h"
#include "os_mem.h"
#include "os_sched.h"

#if OS_MUTEX_ENABLE

#if !OS_MUTEX_DBG_PRINT_ENABLE
#undef os_dbg
#define os_dbg(fmt, ...) do {}while (0)
#endif

static os_err_t mutex_init (os_mutex_t * mutex, int flags) {
    // 初始化等待结构
    os_err_t err = os_event_init(&mutex->event, OS_EVENT_TYPE_MUTEX, flags);
    if (err < 0) {
        os_dbg("create mutex event failed: %s.", "init failed.");
        return err;
    }

    mutex->locked_cnt = 0;
    mutex->owner = OS_NULL;
    mutex->raw_prio = OS_TASK_IDLE_PRIO;         // 设置成无效值
    return OS_ERR_OK;
}

/**
 * 初始化互斥信号量
 * @param 待初始化的互斥信号量
 * @return 初始化结果
*/
os_err_t os_mutex_init (os_mutex_t * mutex) {
    os_param_failed(mutex == OS_NULL, OS_ERR_PARAM);
    return mutex_init(mutex, 0);
}

/**
 * 取消互斥信号量的初始化
 * @param mutex 互斥信号量
*/
os_err_t os_mutex_uninit (os_mutex_t * mutex) {       
    os_param_failed(mutex == OS_NULL, OS_ERR_PARAM);

    // 需要先释放掉，才能销毁
    if (mutex->owner) {
        os_dbg("mutex is locked\n");
        return OS_ERR_LOCKED;
    }

    // 唤醒所有任务后，尝试进行调试
    os_int_status_t status = os_sched_int_lock();
    int cnt = os_event_wait_cnt(&mutex->event);
    os_event_uninit(&mutex->event);
    os_sched_int_unlock(status);

    // 有任务唤醒时，进行调度，以便运行更高优先级的任务
    if (cnt > 0) {
        os_dbg("%d task wakeup, scheduler\n", cnt);
        os_sched_run();
    }
    return OS_ERR_OK;
}

#if OS_MEM_ENABLE

/**
 * 分配一个信号量
 * @param init_cnt 信号量初始值
 * @param max_cnt 信号量最大计数值, 如果为0时表示没有限制
 * @return 创建的信号量
 */
os_mutex_t * os_mutex_create (void) {
    // 分配内存
    os_mutex_t * mutex = os_mem_malloc(sizeof(os_mutex_t));
    if (mutex == OS_NULL) {
        os_dbg("error: alloc mutex failed");
        return OS_NULL;
    }

    // 初始化结构
    os_err_t err = mutex_init(mutex, OS_FLAG_MEM_HEAP);
    if (err < 0) {
        os_dbg("error: init mutex failed.");
        os_mem_free(mutex);
        return OS_NULL;
    }

    return mutex;
}

/**
 * 释放一个信号量
 * @param 需要释放的信号量
 */
os_err_t os_mutex_free (os_mutex_t * mutex) {
    os_param_failed(mutex == OS_NULL, OS_ERR_PARAM);

    os_err_t err = os_mutex_uninit(mutex);
    if (err < 0) {
        return err;
    }

    if (mutex->event.flags & OS_FLAG_MEM_HEAP) {
        os_mem_free(mutex);
    }
    return OS_ERR_OK;
}
#endif

/**
 * 互斥信号量上锁
 * @param mutex 操作的互斥信号量
 * @param ms 等待的时间
 * @return 上锁结果，< 0表示超时或其它错误
*/
os_err_t os_mutex_lock (os_mutex_t * mutex, int ms) {
    os_param_failed(mutex == OS_NULL, OS_ERR_PARAM);

    os_int_status_t status = os_sched_int_lock();

    // 没有其它任务锁定时，自己给上锁一下
    if (mutex->owner == OS_NULL) {
        os_assert(mutex->locked_cnt == 0);

        mutex->locked_cnt = 1;
        mutex->owner = os_task_self();
        mutex->raw_prio = mutex->owner->prio;
        os_sched_int_unlock(status);
        return OS_ERR_OK;
    }

    // 自己反复锁定
    if (mutex->owner == os_task_self()) {
        // 这里不检查计数值了，如果锁定次数太大，上层程序肯定出了问题
        mutex->locked_cnt++;
        // 重复锁定，不算错误
        os_dbg("mutex locked cnt: %d > 1\n", mutex->locked_cnt);
        os_sched_int_unlock(status);

        return OS_ERR_OK;
    }

    // 是否要等待
    if (ms < 0) {
        os_dbg("mutex locked and no wait\n");
        os_sched_int_unlock(status);
        return OS_ERR_LOCKED;
    }

    // 有其它任务在锁定，提升锁定的任务优先级让其尽快运行释放锁
    os_task_t * self = os_task_self();
    os_task_t * owner = mutex->owner;
    if (self->prio < owner->prio) {
        os_dbg("mutex inherit prio happen: task %s, prio from %d to %d\n", 
            owner->name, owner->prio, self->prio);
        os_task_t * owner = mutex->owner;

        // 提升拥有锁的优先级，让其尽快运行
        if (owner->flags & OS_TASK_STATE_RDY) {
            // 就绪队列，移除后再重新插入
            os_sched_remove_ready(owner);
            owner->prio = self->prio;
            os_sched_set_ready(owner);
        } else if (owner->flags & OS_TASK_STATE_WAIT) {
            // 重新插入队列中，从而进行优先级排序
            os_event_t * event = owner->event_wait.event;
            os_event_remove_task(event, owner);
            owner->prio = self->prio;
            os_event_insert_task(event, owner);
        } else {
            // 其它状态，如延时、挂起等无优先级排序
            owner->prio = self->prio;
        }
    }

    // 切换挂起自己
    os_event_wait(&mutex->event, OS_NULL, ms);
    os_sched_int_unlock(status);
    os_sched_run();

    return self->event_wait.err;
}

/**
 * 尝试解锁信号量
 * @param 待解锁的信号量
 * @return 解锁结果，OK解锁成功，其它解锁失败
 */
os_err_t os_mutex_unlock (os_mutex_t * mutex) {
    os_param_failed(mutex == OS_NULL, OS_ERR_PARAM);

    os_int_status_t status = os_sched_int_lock();

    // 已经为非锁定状态
    if (mutex->owner == OS_NULL) {
        os_dbg("mutex is unlocked\n");

        os_sched_int_unlock(status);
        return OS_ERR_UNLOCKED;
    }

    // 不是拥有者释放，认为是非法
    os_task_t * self = os_task_self();
    if (mutex->owner != self) {
        os_dbg("mutex.owner != self\n");
        os_sched_int_unlock(status);
        return OS_ERR_OWNER;
    }

    // 此时计数肯定不为0，减1后计数仍不为0, 直接退出，不需要唤醒等待的任务
    os_assert(mutex->locked_cnt != 0);
    if (--mutex->locked_cnt > 0) {
        // 有重复锁定，不算错误
        os_dbg("mutex.lock_cnt %d > 1\n", mutex->locked_cnt);
        os_sched_int_unlock(status);
        return OS_ERR_OK;
    }

    // 恢复优先级继承机制下的任务原有优先级
    if (mutex->raw_prio != self->prio) {
        os_dbg("mutex inherit prio restore: task %s, prio from %d to %d\n",
            self->name, self->prio,  mutex->raw_prio);
        
        // 当前任务肯定是在运行状态，所以只需要调整就绪队列
        os_sched_remove_ready(self);
        self->prio = mutex->raw_prio;
        os_sched_set_ready(self);
    }

    // 唤醒等待的某个任务
    os_task_t * task = os_event_notify(&mutex->event);
    if (task) {
        // 设置任务的返回值
        task_wait_t * wait = &task->event_wait;
        wait->err = OS_ERR_OK;
        wait->reason = OS_NULL;

        // 调整信号量被新任务所占有
        mutex->owner = task;
        mutex->raw_prio = task->prio;
        mutex->locked_cnt = 1;
        os_sched_int_unlock(status);

        // 如果这个任务的优先级更高，就执行调度，切换过去
        os_sched_run();
    } else {
        mutex->owner = OS_NULL;
        mutex->raw_prio = OS_TASK_IDLE_PRIO;
        os_sched_int_unlock(status);
    }
    return OS_ERR_OK;
}

#if OS_MUTEX_INFO_ENABLE
/**
 * 返回互斥信号量当前上锁的计数值
 * @param 互斥信号量
 * @return 当前上锁的计数值
*/
uint16_t os_mutex_lock_cnt (os_mutex_t * mutex) {
    os_param_failed(mutex == OS_NULL, 0);

    os_int_status_t status = os_sched_int_lock();
    uint16_t cnt = mutex->locked_cnt;
    os_sched_int_unlock(status);
    return cnt;
}

/**
 * 返回互斥信号量当前拥有者
 * @param 互斥信号量
 * @return 当前互斥信号量的拥有者
*/
os_task_t * os_mutex_owner (os_mutex_t * mutex) {
    os_param_failed(mutex == OS_NULL, OS_NULL);

    os_int_status_t status = os_sched_int_lock();
    os_task_t * owner = mutex->owner;
    os_sched_int_unlock(status);
    return owner;
}

/**
 * 获取互斥信号量的任务数量
 * @param sem 信号量
 * @return 互斥锁的最大计数
 */
uint16_t os_mutex_task_cnt (os_mutex_t * mutex) {
    os_param_failed(mutex == OS_NULL, 0);

    os_int_status_t status = os_sched_int_lock();
    uint16_t cnt = os_event_wait_cnt(&mutex->event);
    os_sched_int_unlock(status);
    return cnt;
}
#endif // OS_MUTEX_INFO_ENABLE

#endif


